package com.siao.component.scroller
{
	import com.greensock.TweenMax;
	import com.greensock.easing.Strong;
	import com.siao.display.Align;
	import com.siao.display.LayoutElement;
	import com.siao.display.Margin;
	
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.clearInterval;
	import flash.utils.getTimer;
	import flash.utils.setInterval;
	
	/**
	 * 矩形内容滚动区域组件
	 * @author SiaoLeon
	 */
	public class ScrollRect extends LayoutElement
	{
		// -------------------------------------------------------------------------------------------------------- 静态常量
		
		// -------------------------------------------------------------------------------------------------------- 静态变量
		
		// -------------------------------------------------------------------------------------------------------- 静态方法
		
		// -------------------------------------------------------------------------------------------------------- 静态函数
		
		// -------------------------------------------------------------------------------------------------------- 属性
		
		/**
		 * 最小缓动时长（单位：秒）
		 * @default 0.8
		 */
		public var minEaseTime:Number = 0.8;
		
		/**
		 * 最大缓动时长（单位：秒）
		 * @default 2.5
		 */
		public var maxEaseTime:Number = 2.5;
		
		/**
		 * 拖动内容时检测位移间隔时间（单位：毫秒）
		 * @default  100
		 */
		public var dragRate:Number = 100;
		
		/**
		 * 拖拽松开时缓动系数
		 * @default 300
		 */
		public var dragEase:Number = 300;
		
		/**
		 * 左侧过量拖拽数值
		 * @default 0
		 */
		public var overDragLeft:Number = 0;
		
		/**
		 * 右侧过量拖拽数值
		 * @default 0
		 */
		public var overDragRight:Number = 0;
		
		/**
		 * 顶部过量拖拽数值
		 * @default 0
		 */
		public var overDragTop:Number = 0;
		
		/**
		 * 底部过量拖拽数值
		 * @default 0
		 */
		public var overDragBottom:Number = 0;
		
		/**
		 * 设置过量拖拽数值
		 * @param value
		 */
		public function set overDrage(value:Number):void
		{
			this.overDragLeft = this.overDragRight = this.overDragTop = this.overDragBottom = value;
		}
		
		private var _scrollEasing:Boolean;
		
		/**
		 * 是否开启缓动模式
		 * @return
		 */
		public function get scrollEasing():Boolean
		{
			return this._scrollEasing;
		}
		
		public function set scrollEasing(value:Boolean):void
		{
			this._scrollEasing = value;
			
			this.registerEventListeners();
		}
		
		private var _scrollDrag:Boolean;
		
		/**
		 * 是否开启内容拖拽
		 * @return
		 */
		public function get scrollDrag():Boolean
		{
			return this._scrollDrag;
		}
		
		public function set scrollDrag(value:Boolean):void
		{
			this._scrollDrag = value;
			
			this.registerEventListeners();
		}
		
		private var _contentAlignH:uint;
		
		/**
		 * 显示内容水平对齐方式
		 * @return
		 */
		public function get contentAlignH():uint
		{
			return this._contentAlignH;
		}
		
		public function set contentAlignH(value:uint):void
		{
			this._contentAlignH = value;
			
			this.positionContainer();
		}
		
		private var _contentAlignV:uint;
		
		/**
		 * 显示内容垂直对齐方式
		 * @return
		 */
		public function get contentAlignV():uint
		{
			return this._contentAlignV;
		}
		
		public function set contentAlignV(value:uint):void
		{
			this._contentAlignV = value;
			
			this.positionContainer();
		}
		
		private var _content:DisplayObject;
		
		/**
		 * 显示内容
		 * @return
		 */
		public function get content():DisplayObject
		{
			return (this._content == null) ? new Sprite() : this._content;
		}
		
		public function set content(value:DisplayObject):void
		{
			this._content = value;
			
			this.init();
		}
		
		protected var _maskMargin:Margin = new Margin();
		
		/**
		 * 设置遮罩外边距
		 * @param value
		 */
		public function set maskMargin(value:Number):void
		{
			this._maskMargin.left = this._maskMargin.right = this._maskMargin.top = this._maskMargin.right = value;
			
			this.resize();
		}
		
		/**
		 * 遮罩左侧外边距
		 * @return
		 */
		public function get maskMarginLeft():Number
		{
			return this._maskMargin.left;
		}
		
		public function set maskMarginLeft(value:Number):void
		{
			this._maskMargin.left = value;
			
			this.resize();
		}
		
		/**
		 * 遮罩右侧外边距
		 * @return
		 */
		public function get maskMarginRight():Number
		{
			return this._maskMargin.right;
		}
		
		public function set maskMarginRight(value:Number):void
		{
			this._maskMargin.right = value;
			
			this.resize();
		}
		
		/**
		 * 遮罩顶部外边距
		 * @return
		 */
		public function get maskMarginTop():Number
		{
			return this._maskMargin.top;
		}
		
		public function set maskMarginTop(value:Number):void
		{
			this._maskMargin.top = value;
			
			this.resize();
		}
		
		/**
		 * 遮罩底部外边距
		 * @return
		 */
		public function get maskMarginBottom():Number
		{
			return this._maskMargin.bottom;
		}
		
		public function set maskMarginBottom(value:Number):void
		{
			this._maskMargin.bottom = value;
			
			this.resize();
		}
		
		// -------------------------------------------------------------------------------------------------------- 变量
		
		protected var maskLayer:Sprite;
		
		protected var container:LayoutElement;
		
		private var containerLocalX:Number;
		
		private var containerLocalY:Number;
		
		private var containerStartX:Number;
		
		private var containerStartY:Number;
		
		private var containerMouseInterval:uint;
		
		private var containerMouseTime:Number;
		
		private var tweenX:TweenMax;
		
		private var tweenY:TweenMax;
		
		// -------------------------------------------------------------------------------------------------------- 构造
		
		/**
		 * 构造函数
		 * @param content 显示内容对象
		 */
		public function ScrollRect(content:DisplayObject)
		{
			super();
			
			this._content = content;
			
			this.maskLayer = new Sprite();
			this.maskLayer.mouseEnabled = this.maskLayer.mouseChildren = false;
			this.maskLayer.graphics.beginFill(0X000000);
			this.maskLayer.graphics.drawRect(0, 0, 1, 1);
			this.maskLayer.graphics.endFill();
			this.addChild(this.maskLayer);
			
			this.container = new LayoutElement();
			this.container.mask = this.maskLayer;
			this.addChild(this.container);
		}
		
		// -------------------------------------------------------------------------------------------------------- 方法
		
		override public function clear():void
		{
			for (var i:int = this.numChildren - 1; i >= 0; i--)
			{
				var child:DisplayObject = this.getChildAt(i);
				
				if (child == this.container || child == this.maskLayer)
				{
					continue;
				}
				
				this.removeChild(child);
			}
		}
		
		override public function resize():void
		{
			super.resize();
			
			this.resizeMask();
			this.resizeContainer();
			
			var range:Margin = this.dragRange();
			
			if (this.container.x < range.left)
			{
				this.container.x = range.left;
			}
			
			if (this.container.x > range.right)
			{
				this.container.x = range.right;
			}
			
			if (this.container.y < range.top)
			{
				this.container.y = range.top;
			}
			
			if (this.container.y > range.bottom)
			{
				this.container.y = range.bottom;
			}
		}
		
		/**
		 * 移动内容显示位置，坐标以内容原点计算（显示区域相对于内容左上角的坐标位置）。如开启缓动则缓动位移至该位置。
		 * @param x
		 * @param y
		 */
		public function moveTo(x:Number, y:Number):void
		{
			x = this.maskMarginLeft - x;
			y = this.maskMarginTop - y;
			
			//如果与当前位置一致则返回
			if (this.container.x == x && this.container.y == y)
			{
				return;
			}
			
			//移除所有缓动效果
			TweenMax.killAll();
			
			if (this.scrollEasing)
			{
				//缓动
				var range:Margin = this.dragRange();
				//检测目标位置是否超出移动范围
				var targetX:Number = (x > range.right) ? (range.right + Math.min(this.overDragLeft, (x - range.right) / 2)) : (x < range.left) ? (range.left - Math.min(this.overDragRight, (range.left - x) / 2)) : x;
				var targetY:Number = (y > range.bottom) ? (range.bottom + Math.min(this.overDragTop, (y - range.bottom) / 2)) : (y < range.top) ? (range.top - Math.min(this.overDragBottom, (range.top - y) / 2)) : y;
				
				x = (x > range.right) ? Math.min(x, targetX + targetX - range.right) : (x < range.left) ? Math.max(x, targetX + targetX - range.left) : x;
				y = (y > range.bottom) ? Math.min(y, targetY + targetY - range.bottom) : (y < range.top) ? Math.max(y, targetY + targetY - range.top) : y;
				
				//设置缓动时间
				var time:Number = Math.max(Math.abs(this.container.x - x) / this.maskLayer.width, Math.abs(this.container.y - y) / this.maskLayer.height);
				
				this.tweenX = TweenMax.to(this.container, Math.min(this.maxEaseTime, Math.max(this.minEaseTime, time)), {x: x, ease: Strong.easeOut, onComplete: updatePositionH, onCompleteParams: [targetX], onUpdate: updatePositionH, onUpdateParams: [targetX]});
				this.tweenY = TweenMax.to(this.container, Math.min(this.maxEaseTime, Math.max(this.minEaseTime, time)), {y: y, ease: Strong.easeOut, onComplete: updatePositionV, onCompleteParams: [targetY], onUpdate: updatePositionV, onUpdateParams: [targetY]});
				
			}
			else
			{
				//定位
				this.container.x = x;
				this.container.y = y;
			}
		}
		
		/**
		 * 拖拽内容显示位置，坐标以内容原点计算（显示区域相对于内容左上角的坐标位置）。该方法将直接设置内容坐标，不参与缓动效果。
		 * @param x
		 * @param y
		 */
		public function dragTo(x:Number, y:Number):void
		{
			x = this.maskMarginLeft - x;
			y = this.maskMarginTop - y;
			
			var range:Margin = this.dragRange();
			
			//如果超量拖拽，仿IPHONE半数计算
			x = (x <= range.right) ? x : Math.min(range.right + this.overDragLeft, range.right + (this.mouseX - this.containerLocalX - this.paddingLeft - range.right) / 2);
			x = (x >= range.left) ? x : Math.max(range.left - this.overDragRight, range.left + (this.mouseX - this.containerLocalX - this.paddingLeft - range.left) / 2);
			
			//如果超量拖拽，仿IPHONE半数计算
			y = (y <= range.bottom) ? y : Math.min(range.bottom + this.overDragTop, range.bottom + (this.mouseY - this.containerLocalY - this.paddingTop - range.bottom) / 2);
			y = (y >= range.top) ? y : Math.max(range.top - this.overDragBottom, range.top + (this.mouseY - this.containerLocalY - this.paddingTop - range.top) / 2);
			
			//鼠标拖拽定位
			this.container.x = x;
			this.container.y = y;
		}
		
		// -------------------------------------------------------------------------------------------------------- 函数
		
		override protected final function init():void
		{
			//仅在显示时初始化布局
			if (this.stage == null || !this.visible)
			{
				return;
			}
			
			//布局内容
			this.container.clear();
			this.container.reset();
			this.container.addChild(this.content);
			
			super.init();
			
			this.positionMask();
			this.positionContainer();
			
			this.registerEventListeners();
		}
		
		override protected final function exit():void
		{
			super.exit();
			
			if (this.stage != null)
			{
				this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDragMouseMove);
				this.stage.removeEventListener(MouseEvent.MOUSE_UP, onContentMouseUp);
			}
			
			this.removeEventListeners();
		}
		
		/**
		 * 拖拽范围
		 * @return
		 */
		protected function dragRange():Margin
		{
			var minX:Number;
			var maxX:Number;
			
			switch (this.contentAlignH)
			{
				case Align.LEFT:
					minX = (this.content.width > this.maskLayer.width) ? this.maskMarginLeft + this.maskLayer.width - this.content.width : this.maskMarginLeft;
					maxX = this.maskMarginLeft;
					break;
				case Align.RIGHT:
					minX = this.maskMarginLeft + this.maskLayer.width - this.content.width;
					maxX = (this.content.width > this.maskLayer.width) ? this.maskMarginLeft : this.maskMarginLeft + this.maskLayer.width - this.content.width;
					break;
				default:
					minX = (this.content.width > this.maskLayer.width) ? this.maskMarginLeft + this.maskLayer.width - this.content.width : this.maskMarginLeft + (this.maskLayer.width - this.content.width) / 2;
					maxX = (this.content.width > this.maskLayer.width) ? this.maskMarginLeft : this.maskMarginLeft + (this.maskLayer.width - this.content.width) / 2;
					break;
			}
			
			var minY:Number;
			var maxY:Number;
			
			switch (this.contentAlignV)
			{
				case Align.TOP:
					minY = (this.content.height > this.maskLayer.height) ? this.maskMarginTop + this.maskLayer.height - this.content.height : this.maskMarginTop;
					maxY = this.maskMarginTop;
					break;
				case Align.BOTTOM:
					minY = this.maskMarginTop + this.maskLayer.height - this.content.height;
					maxY = (this.content.height > this.maskLayer.height) ? this.maskMarginTop : this.maskMarginTop + this.maskLayer.height - this.content.height;
					break;
				default:
					minY = (this.content.height > this.maskLayer.height) ? this.maskMarginTop + this.maskLayer.height - this.content.height : this.maskMarginTop + (this.maskLayer.height - this.content.height) / 2;
					maxY = (this.content.height > this.maskLayer.height) ? this.maskMarginTop : this.maskMarginTop + (this.maskLayer.height - this.content.height) / 2;
					break;
			}
			
			return new Margin(minX, minY, maxX, maxY);
		}
		
		/**
		 * 定位遮罩
		 */
		protected function positionMask():void
		{
			this.maskLayer.x = this.paddingLeft + this._maskMargin.left;
			this.maskLayer.y = this.paddingTop + this._maskMargin.top;
		}
		
		/**
		 * 计算遮罩尺寸
		 */
		protected function resizeMask():void
		{
			this.maskLayer.width = Math.max(0, this.width - this.paddingLeft - this.paddingRight - this._maskMargin.left - this._maskMargin.right);
			this.maskLayer.height = Math.max(0, this.height - this.paddingTop - this.paddingBottom - this._maskMargin.top - this._maskMargin.bottom);
		}
		
		/**
		 * 定位容器
		 */
		protected function positionContainer():void
		{
			var range:Margin = this.dragRange();
			this.container.x = (this.contentAlignH == Align.LEFT) ? range.right : (this.contentAlignH == Align.RIGHT) ? range.left : this.maskMarginLeft + (this.maskLayer.width - this.content.width) / 2;
			this.container.y = (this.contentAlignV == Align.TOP) ? range.bottom : (this.contentAlignV == Align.BOTTOM) ? range.top : this.maskMarginTop + (this.maskLayer.height - this.content.height) / 2;
		}
		
		/**
		 * 计算容器尺寸
		 */
		protected function resizeContainer():void
		{
			this.container.width = this.maskLayer.width;
			this.container.height = this.maskLayer.height;
		}
		
		/**
		 * 注册事件侦听
		 */
		protected function registerEventListeners():void
		{
			if (this.scrollDrag)
			{
				this.container.addEventListener(MouseEvent.MOUSE_DOWN, onContentMouseDown, false, 0, true);
			}
			else
			{
				this.container.removeEventListener(MouseEvent.MOUSE_DOWN, onContentMouseDown);
			}
		}
		
		/**
		 * 移除事件侦听
		 */
		protected function removeEventListeners():void
		{
			this.container.removeEventListener(MouseEvent.MOUSE_DOWN, onContentMouseDown);
		}
		
		/**
		 * 更新水平位置
		 */
		protected function updatePositionH(targetX:Number):void
		{
			var range:Margin = this.dragRange();
			
			//左侧超出限定范围时做反响运动
			if (this.container.x >= range.right && this.container.x >= targetX)
			{
				this.tweenX.updateTo({x: range.right, ease: Strong.easeOut, onComplete: updatePositionH, onCompleteParams: [targetX], onUpdate: updatePositionH, onUpdateParams: [targetX]});
			}
			
			//右侧超出限定范围时做反响运动
			if (this.container.x <= range.left && this.container.x <= targetX)
			{
				this.tweenX.updateTo({x: range.left, ease: Strong.easeOut, onComplete: updatePositionH, onCompleteParams: [targetX], onUpdate: updatePositionH, onUpdateParams: [targetX]});
			}
		}
		
		/**
		 * 更新垂直位置
		 */
		protected function updatePositionV(targetY:Number):void
		{
			var range:Margin = this.dragRange();
			
			//顶部超出限定范围时做反响运动
			if (this.container.y > range.bottom && this.container.y > targetY)
			{
				this.tweenY.updateTo({y: range.bottom, ease: Strong.easeOut, onComplete: updatePositionV, onCompleteParams: [targetY], onUpdate: updatePositionH, onUpdateParams: [targetY]});
			}
			
			//底部超出限定范围时做反响运动
			if (this.container.y < range.top && this.container.y < targetY)
			{
				this.tweenY.updateTo({y: range.top, ease: Strong.easeOut, onComplete: updatePositionV, onCompleteParams: [targetY], onUpdate: updatePositionH, onUpdateParams: [targetY]});
			}
		}
		
		/**
		 * 鼠标按下内容时
		 * @param event
		 */
		private function onContentMouseDown(event:MouseEvent):void
		{
			this.stage.addEventListener(MouseEvent.MOUSE_MOVE, onDragMouseMove, false, 0, true);
			this.stage.addEventListener(MouseEvent.MOUSE_UP, onContentMouseUp, false, 0, true);
			
			//移除所有缓动效果
			TweenMax.killAll();
			
			//设定检测鼠标定位间隔
			clearInterval(this.containerMouseInterval);
			this.containerMouseInterval = setInterval(checkMouse, this.dragRate);
			this.checkMouse();
			
			//设定鼠标按下坐标
			this.containerLocalX = this.container.mouseX * this.container.scaleX;
			this.containerLocalY = this.container.mouseY * this.container.scaleY;
			
			this.onDragMouseMove();
		}
		
		/**
		 * 检测鼠标位置
		 */
		private function checkMouse():void
		{
			this.containerStartX = this.stage.mouseX;
			this.containerStartY = this.stage.mouseY;
			this.containerMouseTime = getTimer();
		}
		
		/**
		 * 鼠标抬起内容时
		 * @param event
		 */
		private function onContentMouseUp(event:Event):void
		{
			if (this.stage != null)
			{
				this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, onDragMouseMove);
				this.stage.removeEventListener(MouseEvent.MOUSE_UP, onContentMouseUp);
				this.stage.removeEventListener(Event.MOUSE_LEAVE, onContentMouseUp);
			}
			
			var range:Margin = this.dragRange();
			
			//根据鼠标移动方向进行缓动X坐标定位
			var x:Number = (this.scrollEasing) ? this.container.x + this.dragEase * (this.stage.mouseX - this.containerStartX) / (getTimer() - this.containerMouseTime) : this.container.x;
			x = (this.container.x != x && this.container.x < range.right && this.container.x > range.left) ? x : Math.max(range.left, Math.min(range.right, x));
			
			//根据鼠标移动方向进行缓动Y坐标定位
			var y:Number = (this.scrollEasing) ? this.container.y + this.dragEase * (this.stage.mouseY - this.containerStartY) / (getTimer() - this.containerMouseTime) : this.container.y;
			y = (this.container.y != y && this.container.y < range.bottom && this.container.y > range.top) ? y : Math.max(range.top, Math.min(range.bottom, y));
			
			//清除检测鼠标定位间隔
			clearInterval(this.containerMouseInterval);
			
			//更新内容位置
			this.moveTo(this.maskMarginLeft - x, this.maskMarginTop - y);
			
			if (event is MouseEvent)
			{
				(event as MouseEvent).updateAfterEvent();
			}
		}
		
		/**
		 * 鼠标移动内容时
		 * @param event
		 */
		private function onDragMouseMove(event:MouseEvent=null):void
		{
			var x:Number =  this.mouseX - this.containerLocalX - this.paddingLeft;
			var y:Number = this.mouseY - this.containerLocalY - this.paddingTop;
			
			this.dragTo(this.maskMarginLeft - x, this.maskMarginTop - y);
			
			if (event != null)
			{
				event.updateAfterEvent();
			}
		}
	
	}
}
